/*
 * os3grid.js
 *
 * Copyright (C) 2006 - OS3 srl - http://www.os3.it
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
/*
	OS3Grid v 0.9.2

	by Fabio Rotondo - fsoft ( at ) sourceforge.net

	Layout NG by: Fabio Rotondo and Gabriele Buscone

	0.9.2	- ENH: better Date support. New col type 'date'.

	0.9.1	- ENH: add_row () now accepts a list containing all values.
		- ENH: set_headers () now accepts a list containing all values.

	0.9.0	- ADD:  OS3Grid.parse_full_id () that parses an ID and returns an object with "grid" / "row" / "col"

		  ENH:	os3grid_get_grid () has been replaced by OS3Grid.get ()
		  ENH:  os3grid_set_cell_value () has been replaced by OS3Grid.set_cell_value ()
		  ENH:  removed all DEPRECATED METHODS

		  CHG:  set_col_validation() is now set_col_validator ()

		  DEL:  removed set_row_select()  [ use: cbacks [ 'row-select' ] ]

	0.8.2	- ADD: 
			- del_row_by_attr ( attr_name, value )

		- DEPRECATED METHODS:

			- set_sortable()	-  Use: "sortable" attribute
			- set_highlight()	-  Use: "highlight" attribute
			- set_style ()		-  Use: "current_style" attribute

		- REMOVED:

			- set_scrollbars()
			- set_border ()

	0.8.1	- ADD: div_id for the destination DIV id.

	0.8	-   NEW: new super layout algo. It works only on Firefox / Mozilla browsers.
		    NEW: clear () method

	0.7	- 
		  - NEW: find_row_by_attr ( attr_name, val, start_pos ) - Returns the row_num of the first row matching attr_name == val
		  - NEW: del_row ( row_num )

		  - ENH: New layout engine: headers and rows are now splitted!
		  - ENH: Better column resize algo.
*/

// {{{ OS3Grid ( id )
function OS3Grid ( id )
{
	this.id = id;
	this.div_id = id;

	this._headers = null;		// Array of header names

	// This array stores the custom click callbacks
	this._click_cb = new Array ();

	// ===================================================================
	// PUBLIC ATTRIBUTES
	// ===================================================================
	this.show_deprecation_warnings = true;

	this.start_counter = 0;

	this.show_row_num = false;
	this.width = "300px";
	this.height = "300px";
	this.highlight = false;		// Flag T/F. If T, the highlight will be performed
	this.sortable  = false;		// Flag T/F. if T, rows are sortable (by clicking on columns headers)
	this.sort_on_edit = false;	// Flag T/F. If T, the rows are automatically sorted after editing

	// Colors
	this.cols = { "hover" 	: "#8ec4cf",
		     "rowsel0"	: "#ffa07f",
		     "rowsel1"	: "#df8c6f",
		     "normal0"	: "#ffffff",
		     "normal1"	: "#dfdfdf",
		     "norm0"	: "#ffffff",
		     "norm1"	: "#dfdfdf",
		     "error0"	: "#ff0033",
		     "error1"	: "#cc0033",
		     "warn0"	: "#ffff99",
		     "warn1"	: "#ffff66",
		     "note0"	: "#9aff9a",
		     "note1"	: "#4eee94"
		    };

	// Default style
	this.current_style = "normal";

	this.rows = new Array ();
	this.cbacks = {
		"row-select" : null, // This cback is called when a row is selected / unselected
		"row-click" : null
	};

	// Metodi pubblici
	// {{{ set_headers ( ... )
	this.set_headers 	= function () { 
		var args = arguments;

		if ( args.length == 1 ) args = args [ 0 ];
		this._headers = args;
	};
	// }}}
	// {{{ render ( id )
	this.render  		= function ( id ) {
		if ( id )
		{
			this.id = this.div_id = id;
			OS3Grid._global_data.grid_arr [ this.id ] = this;
		}

		// Row selections are discarted on rendering
		this.rows_selected = new Array ();

		this._create_table ();

		if ( this.onrender ) this.onrender ( this );
	};
	// }}}
	// {{{ add_row ( ... )
	this.add_row     	= function ()
	{
		var arr;
		var arg = arguments;

		if ( arguments.length == 1 ) arg = arguments [ 0 ];

		arr = { "data" : arg, "style" : this.current_style };

		this.rows.push ( arr );
	};
	// }}}
	// {{{ clear ()
	this.clear		= function () { this.rows = new Array (); };
	// }}}
	// {{{ del_row ( row_num )
	this.del_row     	= function ( row_num ) { this.rows.splice ( row_num, 1 ); };
	// }}}
	// {{{ del_row_by_attr ( attr_name, val, all = false )
	this.del_row_by_attr   	= function ( attr_name, val, all ) 
	{ 
		var row_num = this.find_row_by_attr ( attr_name, val );

		while ( row_num != -1 )
		{
			this.del_row ( row_num );
			if ( ! all ) break;
			row_num = this.find_row_by_attr ( attr_name, val );
		}
	};
	// }}}
	// {{{ find_row_by_attr ( attr_name, val, start_pos )
	this.find_row_by_attr   = function ( attr_name, val, start_pos )
	{
		var t, l = this.rows.length;
		var attrs;

		if ( ! start_pos ) start_pos = 0;

		if ( start_pos > l ) return -1;

		for ( t = start_pos; t < l; t ++ )
		{
			attrs = this.rows [ t ] [ 'os3_attrs' ];
			if ( ( ! attrs ) || ( ! attrs [ attr_name ] ) ) continue;

			if ( attrs [ attr_name ] == val ) return t;
		}

		return -1;
	};
	// }}}
	// {{{ get_col_attrs ( col )
	this.get_col_attrs	= function ( col )
	{
		var attrs = this._column_attrs [ col ];
		if ( attrs ) return ( attrs );

		return this._column_attrs [ col ] = new Array ();
	};
	// }}}
	// {{{ get_row ( row_num )
	this.get_row 		= function ( row ) { return this.rows [ row ] [ "data" ]; };
	// }}}
	// {{{ get_row_attrs ( row_num )
	this.get_row_attrs	= function ( row_num )
	{
		if ( ( row_num == undefined ) || ( row_num == -1 )  ) row_num = this.rows.length -1;

		var attrs = this.rows [ row_num ] [ 'os3_attrs' ];

		if ( attrs ) return attrs;

		return this.rows [ row_num ] [ 'os3_attrs' ] = new Array ();
	};
	// }}}
	// {{{ length ()
	this.length		= function () { return this.rows.length; };
	// }}}
	// {{{ set_click_cb ( col, cback )
	this.set_click_cb	= function ( col, callback )
	{
		this._click_cb [ col ] = callback;
	};
	// }}}
	// {{{ set_col_align ( col, align )
	this.set_col_align	= function ( col, align ) { this.get_col_attrs ( col ) [ 'os3_align' ] = align; };
	// }}}
	// {{{ set_col_editable ( col, edit )
	this.set_col_editable	= function ( col, edit ) { this.get_col_attrs ( col ) [ 'os3_edit' ] = edit; };
	// }}}
	// {{{ set_col_render ( col, render )
	this.set_col_render	= function ( col, render ) { this.get_col_attrs ( col ) [ 'os3_render' ] = render; };
	// }}}
	// {{{ set_col_type ( col, type )
	this.set_col_type	= function ( col, type ) { this.get_col_attrs ( col ) [ 'os3_type' ] = type; };
	// }}}
	// {{{ set_col_valid_chars ( col, chars )
	this.set_col_valid_chars = function ( col, chars ) { this.get_col_attrs ( col ) [ 'os3_chars' ] = chars; };
	// }}}
	// {{{ set_col_validation ( col, func )
	this.set_col_validator = function ( col, func ) { this.get_col_attrs ( col ) [ 'os3_validator' ] = func; };
	// }}}
	// {{{ set_row_attr ( row_num, name, val )
	this.set_row_attr	= function ( row_num, name, val )
	{
		if ( ( row_num == undefined ) || ( row_num == -1 )  ) row_num = this.rows.length -1;

		var attrs = this.get_row_attrs ( row_num );

		attrs [ name ] = val;
	};
	// }}}
	// {{{ set_row_color ( col, row_num )
	this.set_row_color	= function ( col, row_num )
	{
		if ( ( row_num == undefined ) || ( row_num == -1 )  ) row_num = this.rows.length -1;

		this.rows [ row_num ] [ 'color' ] = col;
	};
	// }}}
	// {{{ set_row_style ( style, row_num )
	this.set_row_style	= function ( style, row_num )
	{
		if ( ( row_num == undefined ) || ( row_num == -1 )  ) row_num = this.rows.length -1;
		
		this.rows [ row_num ] [ 'style' ] = style;
	};
	// }}}
	// {{{ set_sort_field ( num )
	this.set_sort_field	= function ( num )
	{
		if ( num == this.sort_field ) 
			this.sort_inverted = ! this.sort_inverted;
		else
		{
			this.sort_field = num;
			this.sort_inverted = false;
		}
	}
	// }}}
	// {{{ sort ()
	this.sort 		= function ()
	{
		if ( this.sort_field == -1 ) return;

		var attrs = this.get_col_attrs ( this.sort_field );
		var ctype = attrs [ "os3_type" ];
		var sfunc;

		if ( ! ctype ) ctype = "str";
		sfunc = { "str" : OS3Grid._sort._str,
			  "int" : OS3Grid._sort._int,
			  "ip"  : OS3Grid._sort._ip,
			  "date": OS3Grid._sort._date } [ ctype ];

		OS3Grid._global_data.sort_field = this.sort_field;
		OS3Grid._global_data.sort_inverted = this.sort_inverted;
		this.rows.sort ( sfunc );
		this.render ();
	};
	// }}}
	// {{{ set_cell_click ( fname )
	this.set_cell_click 	= function ( fname ) { this._cell_click = fname; };
	// }}}

	// PRIVATE METHODS
	// {{{ _create_table ()
	this._create_table  = function ()
	{
		var t, l, i, l2, hl = '', cell_click = '', sort_header = '';
		var sb = new String.buffer ();

		l2 = this._headers.length;

		sb.add ( '<div id="' + this.div_id + '_main" class="widget_tableDiv">' );
		sb.add ( '<table id="' + this.div_id + '_table">' );

		// Creo gli headers
		sb.add ( '<thead><tr>' );
		if ( this.show_row_num ) sb.add ( '<td class="tableWidget_headerCell">&nbsp;</td>' );
		for ( t = 0; t < l2; t ++ ) sb.add ( '<td id="' + this.div_id + ':' + t + ':H" ' + sort_header + '>' + this._headers [ t ] + '</td>') ;
		
		// Creo il body
		sb.add ( '</thead><tbody class="scrollingContent">' );

		l = this.rows.length;

		if ( l )
		{
			if ( this.highlight ) hl = ' onmouseover="OS3Grid._row_over(this)" onmouseout="OS3Grid._row_out(this)" ';
			if ( this._cell_click ) cell_click = ' onclick="OS3Grid._cell_click(this)"';

			var fullrow, style, rowcol, data, bgc, attrs, ca, ctype, cell_id, align, v;

			// ==========================================
			// Create Grid Body
			// ==========================================
			for ( t = 0; t < l; t ++ )
			{
				fullrow = this.rows [ t ];
				data	= fullrow [ 'data' ];
				style	= fullrow [ 'style' ];
				rowcol	= fullrow [ 'color' ];

				if ( rowcol )
					bgc = ' bgcolor="' + rowcol + '"';
				else
					bgc = ' bgcolor="' + this.cols [ style + ( t % 2 ) ] + '"';

				sb.add ( '<tr id="' + this.div_id + ":" + t + '" ' + hl + bgc +  '>' );

				if ( this.show_row_num ) 
				{
                        		sb.add (  '<td class="tableWidget_headerCell" align="right"' );
                        		if ( this.cbacks [ 'row-select' ] ) sb.add ( ' onclick="OS3Grid._row_click(this,\'' + this.id + '\',' + t + ')"' )
                        		sb.add ( '>' + ( this.start_counter + t ) + '</td>' );
                		}

				for ( i = 0; i < l2; i ++) 
				{
					attrs = this.get_col_attrs ( i );

					ca = attrs [ "os3_align" ];
					ctype = attrs [ "os3_type" ];
					cell_id = this.div_id + ":" + i + ":" + t;

					if ( ca )
						align = 'align="' + ca + '"';
					else if ( ctype && ( ctype != 'str' ) )
						align = 'align="right"';
					else
						align = "";
				
					sb.add ( '<td class="g_cell" valign="top" ' + align + cell_click );
					if ( attrs [ "os3_edit" ] ) sb.add ( ' ondblclick="OS3Grid._cell_' + attrs [ "os3_edit" ] + '_edit(this)" ' );
					/*
					if ( this.cbacks [ 'row-click' ] )
					{
						// FIXME: o questo codice non serve a niente, oppure manca un pezzo	
					}
					*/
					sb.add ( ' id="' + cell_id + '"' );
					sb.add ( '>' ); 

					if ( attrs [ "os3_render" ] )
						v = attrs [ "os3_render" ] ( data [ i ], cell_id );
					else
						v = data [ i ];

					sb.add ( v );
					sb.add ( '</td>' );
				}

				sb.add ( '</tr>' );
			}
		}

		sb.add( '</tbody></table></div>' );

		var e = document.getElementById ( this.div_id );
		if ( e ) e.innerHTML = sb.get ();
		
		this._initTableWidget ();
	};
	// }}}
	// {{{ _initTableWidget ()
	this._initTableWidget = function ()
	{
		var self = this;
		var width = this.width + '';
		var height = this.height + '';

		var obj = document.getElementById ( this.div_id + "_table" );
		obj.parentNode.className = 'widget_tableDiv';

		if ( navigator.userAgent.indexOf ( 'MSIE' ) >= 0 )
			obj.parentNode.style.overflowY = 'auto';

		obj.style.width = width;
		obj.parentNode.style.width = width;
		obj.parentNode.style.height = height;			

		this._addEndCol ( obj );
		
		obj.cellSpacing = 0;
		obj.cellPadding = 0;
		obj.className = 'tableWidget';
		
		var tHead = obj.getElementsByTagName ( 'THEAD' ) [ 0 ];
		var cells = tHead.getElementsByTagName ( 'TD' );

		var offset = ( this.show_row_num ? 1 : 0 );
		var no = ( this.show_row_num ? 1 : 0 );
		
		for ( ; no < cells.length; no++ )
		{
			cells [ no ].className = 'tableWidget_headerCell';
			cells [ no ].onselectstart = function () { return false; };

			if ( no == cells.length - 1)
				cells [ no ].style.borderRight = '0px';	

			// var attrs = this.get_col_attrs ( no - offset );
			// var col_type = attrs [ "os3_type" ];

			if ( this.sortable )
			{
				cells [ no ].onmouseover = OS3Grid._highlightTableHeader;
				cells [ no ].onmouseout = function () { this.className = 'tableWidget_headerCell'; };
				cells [ no ].onmousedown = OS3Grid._mousedownTableHeader;
				cells [ no ].onmouseup = OS3Grid._highlightTableHeader;
				cells [ no ].onclick = OS3Grid._click;
			}
		}

		var tBody = obj.getElementsByTagName ( 'TBODY' ) [ 0 ];

		if ( document.all && navigator.userAgent.indexOf ( 'Opera' ) < 0 )
		{
			// explorer
			tBody.className = 'scrollingContent';
			tBody.style.display = 'block';			

			//if ( navigator.userAgent.indexOf ( "MSIE 7" ) >= 0 )
			//	obj.parentNode.style.overflowY = 'scroll';
		} else {
			// firefox + opera
			var sc_h = obj.parentNode.clientHeight - tHead.offsetHeight;

			// FIXME: determinare la dimensione in base all'altezza della Grid
			//        e non con un semplice "250" ...
			if ( sc_h <= 0 ) sc_h = "250";

			tBody.className = 'scrollingContent';
			tBody.style.height = sc_h + 'px';

			if ( navigator.userAgent.indexOf ( 'Opera' ) >= 0 )
				obj.parentNode.style.overflow = 'auto';
		}
	};
	// }}}
	// {{{ _addEndCol ( table )
	this._addEndCol = function ( table )
	{
		if ( document.all ) return;
		
		var rows = table.getElementsByTagName ( 'TR' );
		for ( var no = 0; no < rows.length; no++ )
		{
			var cell = rows [ no ].insertCell ( -1 );
			cell.innerHTML = ' ';
			cell.style.width = '13px';
			cell.width = '13';
		}
	};
	// }}}

	// PRIVATE ATTRS
	this.sort_field = -1;
	this.sort_inverted = false;
		
	this._row_style = new Array ();
	this._column_attrs = new Array ();

	// Bind element to the os3_grid_array
	OS3Grid._global_data.grid_arr [ this.id ] = this;
}
// }}}
// {{{ _global_data_struct 
OS3Grid._global_data_struct = function ()
{
	this.grid_arr = new Array ();
	this.sort_field = 0;
	this.sort_inverted = 0;
};
// }}}

// Flag T/F to know if the current browser is the almighty bugged Internet Exploder.
OS3Grid._is_ie = ( navigator.userAgent.indexOf ( 'MSIE' ) >= 0 );
OS3Grid._global_data = new OS3Grid._global_data_struct ();


// =====================================================================================
// STATIC FUNCTIONS
// =====================================================================================
OS3Grid.parse_full_id = function ( full_id )
{
	var v = full_id.split ( ":" );

	var grid_name = v [ 0 ];
	if ( grid_name.match ( /@/ ) )
		grid_name = grid_name.split ( "@" ) [ 1 ];

	var grid = OS3Grid._global_data.grid_arr [ grid_name ];
	

	return { "grid-name" : grid_name,
		 "grid" : grid,
		 "col"  : parseInt ( v [ 1 ], 10 ),
		 "row"  : parseInt ( v [ 2 ], 10 ) };
};

// {{{ get ( id )
OS3Grid.get = function ( id )
{
	return OS3Grid._global_data.grid_arr [ id ];
};
// }}}
// {{{ set_cell_value ( full_id, val )
OS3Grid.set_cell_value = function ( full_id, val )
{
	var v = full_id.split ( ":" );
        var g = OS3Grid.get ( v [ 0 ] );
	if ( ! g ) return 0
        var r = g.get_row ( v [ 2 ] );

        r [ v [ 1 ] ] = val;

	return g
}
// }}}
// {{{ _row_over ( row )
OS3Grid._row_over = function ( row )
{
	var old_col = row.style.backgroundColor;
	var hover_col;
	var grid = OS3Grid.parse_full_id ( row.id ) [ 'grid' ];

	if ( row.firstChild.id )
		hover_col = grid.cols [ "hover" ];
	else {
		hover_col = grid.cols [ "hover" ];
	}

	if ( ( row.selected ) || ( old_col == hover_col ) ) return;

	row.old_color = old_col;
	row.style.backgroundColor = hover_col;
};
// }}}
// {{{ _row_out ( row )
OS3Grid._row_out = function ( row ) 
{ 
	if ( ! row.selected ) row.style.backgroundColor = row.old_color; 
};
// }}}
// {{{ _cell_click ( cell )
OS3Grid._cell_click = function ( cell )
{
	var v = cell.id.split ( ":" );
	var grid = OS3Grid.parse_full_id ( cell.id ) [ "grid" ]; 
	var val, sel;

	val = grid.rows [ v [ 2 ] ] [ "data" ] [ v [ 1 ] ];

	sel = grid._cell_click ( grid, cell, v [ 2 ], v [ 1 ], val );
	
	if ( sel )
	{
		cell.old_border = cell.style.borderColor;
		cell.style.borderColor = grid.cols [ "rowsel" + ( v [ 2 ] % 2 ) ];
	} else
		cell.style.borderColor = cell.old_border;

	cell.selected = sel;
};
// }}}
// {{{ _click ()
OS3Grid._click = function ()
{
/*
	var name, num, res, grid;

	res = this.id.split ( ":" );
	name = res [ 0 ];
	num  = res [ 1 ];
*/
	var data = OS3Grid.parse_full_id ( this.id );

	this.style.cursor = "wait";

	setTimeout ( function () {
		var grid;
		grid = data [ 'grid' ]; // OS3Grid._global_data.grid_arr [ name ];
		grid.set_sort_field ( data [ 'col' ] );
		grid.sort ();
	}, 50 );
};
// }}}

// ===================================================================
// EDIT FUNCTIONS
// ===================================================================
// {{{ _edit_abort_or_blur ( input, cell_id, evt )
OS3Grid._edit_abort_or_blur = function ( input, cell_id, evt )
{
        evt = (evt) ? evt : event;

	var ccode = ( evt.charCode ) ? evt.charCode : ( ( evt.which ) ? evt.which : evt.keyCode );
        var ch = String.fromCharCode ( ccode );

	// User confirmed input by pressing "enter key"
	if ( ccode == 13 ) return input.blur ();

	// User aborted input
	if ( ccode == 27 )
	{
		var v = cell_id.split ( ":" );
		var grid = OS3Grid.parse_full_id ( cell_id ) [ 'grid' ];
		input.value = grid.rows [ v [ 2 ] ] [ "data" ] [ v [ 1 ] ];
		input.blur ();
	}

	return true;
};
// }}}
// {{{ _edit_end ( input, cell_id )
OS3Grid._edit_end = function ( input, cell_id )
{
	var v = cell_id.split ( ":" );
	var grid = OS3Grid.parse_full_id ( cell_id ) [ 'grid' ]; 
	var oldv = grid.rows [ v [ 2 ] ] [ "data" ] [ v [ 1 ] ];
	var attrs = grid.get_col_attrs ( v [ 1 ] );

	if ( oldv != input.value )
	{
		if ( attrs [ "os3_validator" ] )
			if ( attrs [ "os3_validator" ] ( input.value ) == false )
			{
				alert ( "Invalid input: " + input.value );
				return input.focus ();
			}

		grid.rows [ v [ 2 ] ] [ "data" ] [ v [ 1 ] ] = input.value;
		if ( grid.onchange ) grid.onchange ( grid, v [ 1 ], v [ 2 ], input.value );
		if ( grid.sort_on_edit ) grid.sort ();
	}

	return grid.render ();
};
// }}}
// {{{ _cell_txt_edit ( cell )
OS3Grid._cell_txt_edit = function ( cell )
{
	var v = cell.id.split ( ":" );
	var grid = OS3Grid.parse_full_id ( cell.id ) [ 'grid' ];
	var val;
	var s, el, size;
	var attrs = grid.get_col_attrs ( v [ 2 ] );
	var type = attrs [ "os3_type" ];

	if ( ! type ) type = 'str';

	val = String ( grid.rows [ v [ 2 ] ] [ "data" ] [ v [ 1 ] ] );

	s  = '<input type="text" id="grid_edit_cell" value="' + val + '" ';
	s += ' onblur="OS3Grid._edit_end ( this, \'' + cell.id + '\' )" ';
	s += ' onfocus="this.select()" ';


	if ( attrs [ "os3_chars" ] )
		s += 'onkeypress="return OS3Grid._edit_filter_chars ( event, \'' + attrs [ "os3_chars" ] + '\' )" ';

	s += ' onkeydown="OS3Grid._edit_abort_or_blur(this, \'' + cell.id + '\', event)" ';
	s += ' size="'+ val.length + '" ';
	s += ' class="g_edit_box" ';
	if ( type == 'int' ) s += ' style="text-align: right;" ';
	s += '/>';

	cell.innerHTML = s;

	el = document.getElementById ( "grid_edit_cell" );
	el.focus ();
};
// }}}
// {{{ _edit_filter_chars ( evt, valids )
OS3Grid._edit_filter_chars = function ( evt, valids ) 
{
        evt = (evt) ? evt : event;

        if ( evt.charCode < 32 ) return true;
                                                                                                                                                   
        var ccode = ( evt.charcode ) ? evt.charcode : ( ( evt.which ) ? evt.which : evt.keycode );
        var ch = String.fromCharCode ( ccode ).toLowerCase ();

	valids = valids.toLowerCase ();

        if ( valids.indexOf ( ch ) == -1 ) return false;

        return true;
};
// }}}

// ===================================================================
// Row selection function
// ===================================================================
// {{{ _row_click ( cell, grid_id, row_num )
OS3Grid._row_click = function ( cell, grid_id, row_num )
{
	var grid = OS3Grid._global_data.grid_arr [ grid_id ];
	var is_selected = grid.rows_selected [ row_num ];

	if ( ! is_selected ) is_selected = false;

	if ( is_selected )
	{
		is_selected = false;
	} else {
		is_selected = true;
	}

	if ( is_selected )
		grid.rows_selected [ row_num ] = is_selected;
	else
		delete grid.rows_selected [ row_num ];
		

	if ( grid.cbacks [ 'row-select' ] ) grid.cbacks [ 'row-select' ] ( grid, row_num, is_selected );
};
// }}}

// =====================================================================================
// PRIVATE
// =====================================================================================
// Sort Functions
OS3Grid._sort = {};

// {{{ sort: int ( a, b )
OS3Grid._sort._int = function ( a, b )
{
	var res = 0;
	var v1, v2;

	v1 = parseInt ( a [ "data" ][ OS3Grid._global_data.sort_field ], 10  );
	v2 = parseInt ( b [ "data" ][ OS3Grid._global_data.sort_field ], 10  );

	if  ( v1 < v2 ) res = -1;
	else if ( v1 > v2 ) res = 1;

	if ( OS3Grid._global_data.sort_inverted ) res = -res;

	return res;
};
// }}}
// {{{ sort: str ( a, b )
OS3Grid._sort._str = function ( a, b )
{
	var res = 0;
	var v1, v2;

	v1 = a [ "data" ][ OS3Grid._global_data.sort_field ];
	v2 = b [ "data" ][ OS3Grid._global_data.sort_field ];

	if ( v1 < v2 ) res = -1;
	else if ( v1 > v2 ) res = 1;

	if ( OS3Grid._global_data.sort_inverted ) res = -res;

	return res;
};
// }}}
// {{{ sort: ip ( a, b )
OS3Grid._sort._ip = function ( a, b )
{
	var res = 0;
  	var v1, v2, vA, vB;

  	v1 = a [ "data" ][ OS3Grid._global_data.sort_field ];
  	v2 = b [ "data" ][ OS3Grid._global_data.sort_field ];

  	if ( v1 == v2 ) return 0;

  	vA = v1.split(".");
  	vB = v2.split(".");

  	for ( i = 0; i < 4; i++ )
  	{
    		if ( parseInt ( vA [ i ] ) < parseInt ( vB [ i ] ) )
    		{
      			res = -1;
      			break;
    		} else if ( parseInt ( vA [ i ] ) > parseInt ( vB [ i ] ) )
    		{
      			res = 1;
      			break;
    		}
  	}

  	if ( OS3Grid._global_data.sort_inverted ) res = -res;
  	return res;
};
// }}}
// {{{ sort: date ( a, b )
OS3Grid._sort._date = function ( a, b )
{
	var res = 0;
  	var v1, v2, vA, vB;

  	v1 = a [ "data" ][ OS3Grid._global_data.sort_field ];
  	v2 = b [ "data" ][ OS3Grid._global_data.sort_field ];

  	if ( v1 == v2 ) return 0;

	if ( v1 > v2 ) res = 1;
	if ( v1 < v2 ) res = -1;

  	if ( OS3Grid._global_data.sort_inverted ) res = -res;
  	return res;
};
// }}}


OS3Grid._highlightTableHeader = function ()
{
	this.className = 'tableWigdet_headerCellOver';
	if ( document.all )
	{	
		// I.E fix for "jumping" headings
		var divObj = this.parentNode.parentNode.parentNode.parentNode;
		this.parentNode.style.top = divObj.scrollTop + 'px';
	}
};

OS3Grid._mousedownTableHeader = function ()
{
	this.className = 'tableWigdet_headerCellDown';

	if ( document.all )
	{
		// I.E fix for "jumping" headings
		var divObj = this.parentNode.parentNode.parentNode.parentNode;
		this.parentNode.style.top = divObj.scrollTop + 'px';
	}		
};
